#ifndef LOMCONTEXTHH
#define LOMCONTEXTHH
#include "lom-instance-manager.hh"
#include "lom-events.hh"
#include "lom-class-manager.hh"
#include "lom-value-factory.hh"
#include "lom-instance-factory.hh"
#include "lom-value.hh"
#include "lom-transaction-system.hh"

namespace lom
{
    class ILomContext
    {
    public:
	virtual ~ILomContext(){}
	virtual ILomInstanceManager* getInstanceManager() = 0;
	virtual LomClassManager* getClassManager() = 0;
	virtual ILomEventSystem* getEventSystem() = 0;
	virtual ILomValueFactory* getValueFactory() = 0;
	virtual ILomInstanceFactory* getInstanceFactory() = 0;
	virtual ILomTransactionSystem* getTransactionSystem() = 0;
	
	virtual void setRootObject( const licada::TNameStr& name, const LomValue& val ) = 0;
	/**
	   This never fails, it just returns NULL if no root object is found.
	 */
	virtual const LomValue* getRootObject( const licada::TNameStr& name ) = 0;
	virtual const TNameValueMap& getRootObjectMap() const = 0;
    };
    /**
       This encapsulates everything you need for a lom-world.  
     */
    class LomContext : public ILomContext
    {
	LomInstanceManager instanceMgr;
	LomClassManager classMgr;
	LomEventSystem eventSys;
	LomValueFactory valueFactory;
	LomInstanceFactory instanceFactory;
	TNameValueMap rootObjectMap;
	LomTransactionSystem transactionSystem;

    public:
	void setup() {
	    instanceMgr.setEventHandler( &eventSys );
	    classMgr.setEventHandler( &eventSys );
	    valueFactory.setup( &instanceMgr );
	    instanceFactory.setup( &instanceMgr, &valueFactory, &eventSys );
	    transactionSystem.setupLomTransactionSystem( &valueFactory, &eventSys );
	}
	
	virtual ~LomContext() {
	    reset();
	}
	/**
	   Order is important.  If the value factory is reset before
	   the instance manager or the class manager, then instances
	   will be referencing deleted lom-values when they are being
	   cleaned up.  Pretty much, everything needs to be cleaned
	   up before the value factory is cleaned up.
	 */
	virtual void reset() {
	    instanceMgr.reset();
	    classMgr.reset();
	    std::for_each( rootObjectMap.begin(), rootObjectMap.end()
			   , boost::bind( &LomContext::clearObject, this, _1 ) );
	    rootObjectMap.clear();
	    transactionSystem.resetLomTransactionSystem();
	    valueFactory.reset();
	}
	
	virtual ILomInstanceManager* getInstanceManager() { return &instanceMgr; }
	virtual LomClassManager* getClassManager() { return &classMgr; }
	virtual ILomEventSystem* getEventSystem() { return &eventSys; }
	virtual ILomValueFactory* getValueFactory() { return &valueFactory; }
	virtual ILomInstanceFactory* getInstanceFactory() { return &instanceFactory; }
	virtual ILomTransactionSystem* getTransactionSystem() { return &transactionSystem; }
	
	virtual void setRootObject( const licada::TNameStr& name, const LomValue& val ) {
	    TNameValueMap::iterator find = rootObjectMap.find( name );
	    if ( find != rootObjectMap.end() )
		clearObject( *find );
	    LomValue* setVal = val.clone( &valueFactory );
	    rootObjectMap.insert( std::make_pair( name, setVal ) );
	    eventSys.rootObjectSet( name.c_str(), *setVal );
	}
	
	virtual const LomValue* getRootObject( const licada::TNameStr& name ) {
	    TNameValueMap::iterator find = rootObjectMap.find( name );
	    if ( find != rootObjectMap.end() )
		return find->second;
	    return NULL;
	}
	
	virtual const TNameValueMap& getRootObjectMap() const { 
	    return rootObjectMap; 
	}

	
	virtual LomEventSystem* getLomEventSystem() { return &eventSys; }

    private:
	inline void clearObject( std::pair<const licada::TNameStr,LomValue*>& value ) {
	    if ( value.second )
		valueFactory.destroy( value.second );
	    value.second = NULL;
	}
    };
}
#endif
